package org.gamma.gwt;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.rpc.AsyncCallback;
import java.util.ArrayList;

/**
 * Arbitrates messages between the event-driven message queue and callbacks.
 */
public class ResponseBroker implements EventListener {
    private final ArrayList<AsyncCallback<JavaScriptObject>> callbacks = new ArrayList<AsyncCallback<JavaScriptObject>>();
    
    private static ResponseBroker instance = null;
    protected static ResponseBroker getInstance() {
        if(instance == null) {
            instance = new ResponseBroker();
        }
        return instance;
    }

    private ResponseBroker() {
        Element domElement = getGammaSink();
        com.google.gwt.user.client.Element cliElement = (com.google.gwt.user.client.Element)domElement;
        DOM.setEventListener(cliElement, this);
        DOM.sinkEvents(cliElement, Event.ONCLICK);
    }
    
    /**
     * Register a callback for a response.
     * @return The index on the message queue where the responder will place its message.
     */
    protected synchronized int subscribe(AsyncCallback<JavaScriptObject> callback) {
    	JsArray<JavaScriptObject> messageArray = getGammaMessages();
        int idx = 0;
        JavaScriptObject obj = messageArray.get(idx);
        // Find the lowest un-used index. This works even if we fall off the end.
        while(obj != null) {
        	++idx;
        	obj = messageArray.get(idx);
        }

        obj = JavaScriptObject.createObject(); // this blank object is the "response"; the applet sets values on it
        messageArray.set(idx, obj);
        
        final int iNeededCapacity = idx + 1;
        // Using ensureCapacity just doesn't work here for whatever reason.
        while(callbacks.size() < iNeededCapacity) callbacks.add(null);
        
        callbacks.set(idx, callback);
        
        return idx;
    }
    
    private static native Element getGammaSink() /*-{
        return $wnd.gamma.sink;
    }-*/;
    
    private static native JsArray<JavaScriptObject> getGammaMessages() /*-{
        return $wnd.gamma.messages;
    }-*/;

    public void onBrowserEvent(Event evt) {
        if (DOM.eventGetType(evt) == Event.ONCLICK) { // onFocus arbitrarily chosen for publish notification
            final int idx = evt.getClientX(); // Arbitrarily chosen to hold the index.
            final AsyncCallback<JavaScriptObject> callback = callbacks.get(idx);
            JavaScriptObject result = getGammaMessages().get(idx);
            callback.onSuccess(result);
            // clear this slot for future use
            getGammaMessages().set(idx, null);
        } 
    }

}
